package com.cloudfun.campusshare.service.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;
import com.cloudfun.campusshare.common.constant.*;
import com.cloudfun.campusshare.common.exception.BusinessException;
import com.cloudfun.campusshare.common.model.dto.*;
import com.cloudfun.campusshare.common.model.vo.OrderInfo4PayCallBackVO;
import com.cloudfun.campusshare.configuration.property.HsqProperty;
import com.cloudfun.campusshare.entity.BusinessOrderRelationEntity;
import com.cloudfun.campusshare.entity.DeviceEntity;
import com.cloudfun.campusshare.entity.MerchantBankRegisterEntity;
import com.cloudfun.campusshare.entity.OrderEntity;
import com.cloudfun.campusshare.repo.BusinessOrderRelationEntityRepo;
import com.cloudfun.campusshare.repo.DeviceEntityRepo;
import com.cloudfun.campusshare.repo.MerchantBackRegisterEntityRepo;
import com.cloudfun.campusshare.repo.OrderEntityRepo;
import com.cloudfun.campusshare.sender.DelayMessage;
import com.cloudfun.campusshare.sender.DelayTaskMessageSender;
import com.cloudfun.campusshare.service.BillService;
import com.cloudfun.campusshare.service.DeviceModeService;
import com.cloudfun.campusshare.service.DeviceOrderService;
import com.cloudfun.campusshare.service.HsqService;
import com.cloudfun.campusshare.util.DateUtil;
import com.cloudfun.campusshare.util.hsq.RsaUtil;
import com.cloudfun.campusshare.util.hsq.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import springfox.documentation.spring.web.json.Json;

import javax.annotation.Resource;
import java.nio.charset.Charset;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author : zhangyongjie
 * @version : 1.0
 * @createTime : 2021/2/7 15:39
 * @description :
 */
@Slf4j
@Service
public class HsqServiceImpl implements HsqService {

    @Autowired
    private HsqProperty hsqProperty;

    @Autowired
    private RestTemplate restTemplate;

    @Resource
    private OrderEntityRepo orderEntityRepo;

    @Resource
    private DeviceEntityRepo deviceEntityRepo;

    @Autowired
    private DelayTaskMessageSender delayTaskMessageSender;

    @Resource
    private MerchantBackRegisterEntityRepo merchantBackRegisterEntityRepo;

    @Autowired
    private DeviceOrderService deviceOrderService;

    @Resource
    private BusinessOrderRelationEntityRepo businessOrderRelationEntityRepo;
    @Override
    public TradePreCreateRespDTO createUnifyTradeOrder(TradePreCreateDTO tradePreCreateDTO) {
        try{
            BaseHsqResponse resp = executeReq(tradePreCreateDTO, MethodType.POLYMERIZE_MAIN_SWEPTN);
            return handleHsqResult(resp,TradePreCreateRespDTO.class);
        }catch (Exception e){
            log.info("预创建交易单失败",e);
        }
        return null;
    }

    /**
     * 处理慧收钱接口返回结果。
     * @param response
     * @param tClass
     * @param <T>
     * @return
     */
    private <T> T handleHsqResult(BaseHsqResponse response,Class<T> tClass){
        String result = response.getResult();
        String deCryContent = RsaUtil.decryptByPubCerFile(result);
        return JSON.parseObject(RsaUtil.base64Decode(deCryContent), tClass);
    }

    @Override
    public String handleCallback(BaseHsqReq callbackReq) {
        //好家伙，不需要验签
//        verifySign(callbackReq,callbackReq.getSign());

        String signContent = callbackReq.getSignContent();
        String deCryContent = RsaUtil.decryptByPubCerFile(signContent);
        CallbackResponseDTO callbackResponseDTO = JSON.parseObject(RsaUtil.base64Decode(deCryContent), CallbackResponseDTO.class);
        log.info("慧支付服务端回调信息：{}",callbackResponseDTO);
        if(!"SUCCESS".equals(callbackResponseDTO.getOrderStatus())){
            log.info("慧收钱支付失败，当前订单状态为:{},错误信息为:{}",callbackResponseDTO.getOrderStatus(),callbackResponseDTO.getRespMsg());
            return "SUCCESS";
        }

        OrderEntity order = orderEntityRepo.findByOrderNo(callbackResponseDTO.getTransNo());
        if(OrderStatus.WAIT_PAY.equals(order.getOrderStatus())){
            if(String.valueOf(order.getSettlementAmount()).equals(callbackResponseDTO.getOrderAmt())){
                order.setTradeTime(String.valueOf(System.currentTimeMillis()));
                //设置为已支付
                order.setOrderStatus(OrderStatus.PAID);
                //设置实际支付金额
                order.setActualPayAmount(order.getSettlementAmount());
                // 保存订单
                OrderEntity save = orderEntityRepo.save(order);

                //保存账单
                deviceOrderService.createBill(order,callbackResponseDTO);
                //5分钟后，如果设备没有在运行状态（筒自洁或者正常启动），则启动设备
                DeviceEntity device = deviceEntityRepo.findById(save.getDeviceId()).orElseThrow(() -> new BusinessException(Code.NODATA));
                delayTaskMessageSender.sendDelayMessage(new DelayMessage.Builder().orderId(save.getId()).deviceId(device.getId())
                        .type(DelayMsgType.EXPIRE_USER_PICK)
                        .userId(order.getCustomerId())
                        .consumerType(DelayConsumeAction.START_DEVICE)
                        .build(), 0);

            }else{
                log.info("慧支付毁掉通知, 订单金额:{}和支付金额:{}不匹配",order.getSettlementAmount(),callbackResponseDTO.getOrderAmt());
                return "FAIL";
            }
        }

        return "SUCCESS";
    }

    @Override
    public String handleImageDataUploadCallback(BaseHsqReq baseHsqReq) {
        String signContent = baseHsqReq.getSignContent();
        String deCryContent = RsaUtil.decryptByPubCerFile(signContent);

        log.info("慧收钱影像资料批量上传接口回调:{}",deCryContent);
        HsqImageDataCallBackDTO hsqImageDataCallBackDTO = JSON.parseObject(deCryContent, HsqImageDataCallBackDTO.class);

        ImageDataBatchUploadDTO.Qualification qualification = JSON.parseObject(hsqImageDataCallBackDTO.getQualification(), ImageDataBatchUploadDTO.Qualification.class);
        MerchantBankRegisterEntity merchantRegister = merchantBackRegisterEntityRepo.findByBatchNo(hsqImageDataCallBackDTO.getBatchNo());

        if(org.apache.commons.lang3.StringUtils.isNotBlank(hsqImageDataCallBackDTO.getRespMsg())){
            //说明资料有问题，状态改为失败
            merchantRegister.setStatus(MerchantRegisterStatus.FAIL);
            merchantRegister.setResult(hsqImageDataCallBackDTO.getRespMsg());
            merchantBackRegisterEntityRepo.save(merchantRegister);
            return "SUCCESS";
        }
        switch (merchantRegister.getRegisterType()){
            case PERSONAL:
                HsqMerchantRegisterDTO hsqMerchantRegisterDTO = new HsqMerchantRegisterDTO();
                BeanUtils.copyProperties(merchantRegister,hsqMerchantRegisterDTO);
                hsqMerchantRegisterDTO.setIdCardFrontImg(qualification.getIdCardFrontImg());
                hsqMerchantRegisterDTO.setIdCardFrontImg(qualification.getIdCardBackImg());
                HsqMerchantRegisterRespDTO merchantRegisterResp = createMerchantRegister(hsqMerchantRegisterDTO);
                log.info("个人进件资料上传，结果:{}", JSON.toJSONString(merchantRegisterResp));
                merchantRegister.setStatus(MerchantRegisterStatus.valueOf(merchantRegisterResp.getStatus()));
                break;
            case ORGANIZATION:
                HsqMerchantRegister4OrgDTO hsqMerchantRegister4OrgDTO = new HsqMerchantRegister4OrgDTO();
                BeanUtils.copyProperties(merchantRegister,hsqMerchantRegister4OrgDTO);
                hsqMerchantRegister4OrgDTO.setLegalName(merchantRegister.getName());
                hsqMerchantRegister4OrgDTO.setLegalPhone(merchantRegister.getPhone());
                hsqMerchantRegister4OrgDTO.setLegalIdcardNo(merchantRegister.getCertId());
                hsqMerchantRegister4OrgDTO.setContactName(merchantRegister.getName());
                hsqMerchantRegister4OrgDTO.setContactPhone(merchantRegister.getPhone());
                hsqMerchantRegister4OrgDTO.setContactIdcardNo(merchantRegister.getCertId());
                hsqMerchantRegister4OrgDTO.setIdCardFrontImg(qualification.getIdCardFrontImg());
                hsqMerchantRegister4OrgDTO.setIdCardFrontImg(qualification.getIdCardBackImg());
                hsqMerchantRegister4OrgDTO.setBusinessLicenseImg(qualification.getBusinessLicenseImg());
                HsqMerchantRegisterRespDTO merchantRegister4Org = createMerchantRegister4Org(hsqMerchantRegister4OrgDTO);
                log.info("企业进件资料上传，结果:{}", JSON.toJSONString(merchantRegister4Org));
                merchantRegister.setStatus(MerchantRegisterStatus.valueOf(merchantRegister4Org.getStatus()));
        }
        merchantBackRegisterEntityRepo.save(merchantRegister);
        return "SUCCESS";
    }

    @Override
    public HsqMerchantRegisterRespDTO createMerchantRegister(HsqMerchantRegisterDTO hsqMerchantRegisterDTO) {
        try{
            BaseHsqResponse resp = executeReq(hsqMerchantRegisterDTO, MethodType.PERSON_STORE_REPORT, "register");
            return handleHsqResult(resp,HsqMerchantRegisterRespDTO.class);
        }catch (Exception e){
            log.info("进件申请失败",e);
        }
        return null;
    }

    @Override
    public HsqMerchantRegisterRespDTO createMerchantRegister4Org(HsqMerchantRegister4OrgDTO hsqMerchantRegister4OrgDTO) {
        try{
            BaseHsqResponse resp = executeReq(hsqMerchantRegister4OrgDTO, MethodType.ORG_STORE_REPORT, "register");
            return handleHsqResult(resp,HsqMerchantRegisterRespDTO.class);
        }catch (Exception e){
            log.info("进件申请失败",e);
        }
        return null;
    }

    @Override
    public ImageDataBatchUploadRespDTO uploadImageData(ImageDataBatchUploadDTO imageDataBatchUploadDTO) {
        try{
            BaseHsqResponse resp = executeReq(imageDataBatchUploadDTO, MethodType.BATCH_FILE_UPLOAD, "upload");
            return handleHsqResult(resp,ImageDataBatchUploadRespDTO.class);
        }catch (Exception e){
            log.info("进件申请失败",e);
        }
        return null;
    }

    @Override
    public OrderInfo4PayCallBackVO queryOrderInfo(String weChatOrderNo) {
        OrderInfo4PayCallBackVO result = new OrderInfo4PayCallBackVO();
        BusinessOrderRelationEntity businessOrderRelationEntity = businessOrderRelationEntityRepo.findByWeChatOrderNo(weChatOrderNo);
        if (Objects.isNull(businessOrderRelationEntity)){
            throw new BusinessException(Code.NODATA);
        }
        OrderEntity orderEntity = orderEntityRepo.findById(businessOrderRelationEntity.getOrderId()).get();
        LocalDateTime localDateTime = Instant.ofEpochMilli(Long.valueOf(orderEntity.getCreateTime())).atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
        result.setCreateTime(localDateTime);
        result.setDeviceId(orderEntity.getDeviceId());
        result.setOrderId(orderEntity.getId());
        DeviceEntity deviceEntity = deviceEntityRepo.findById(orderEntity.getDeviceId()).get();
        result.setDeviceName(deviceEntity.getDeviceName());
        result.setOrderName(orderEntity.getOrderName());
        result.setImei(deviceEntity.getImei());
        return result;
    }


    private BaseHsqResponse executeReq(Object reqParam, MethodType type,String... suffix){
        BaseHsqReq req = BaseHsqReq.builder().method(type.name())
                .format(FormatType.JSON)
                .version("1.0")
                .merchantNo(hsqProperty.getMerchantId())
                .build();

        String base64 = RsaUtil.base64Encode(JSON.toJSONString(reqParam));
        String signContent = "";
        try {
            signContent  = RsaUtil.encryptByPrivateKey(base64,hsqProperty.getPassword());
        } catch (Exception e) {
            log.info("RSA加密失败,加密内容:{}",base64);
            e.printStackTrace();
        }

        req.setSignContent(signContent);
        req.setSignType("CFCA");
        signReq(req);

        JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(req));
        MultiValueMap<String,Object> urlVariables = new LinkedMultiValueMap<>();
        jsonObject.entrySet().forEach(x ->{
            urlVariables.add(x.getKey(),x.getValue());
        });

        ResponseEntity<BaseHsqResponse> response = restTemplate.postForEntity((suffix == null || suffix.length == 0)?hsqProperty.getGateway()
                :hsqProperty.getRegisterGateway().replace("#{suffix}",suffix[0]), urlVariables, BaseHsqResponse.class);
        BaseHsqResponse result = response.getBody();
        if(!result.getSuccess()){
            log.info("请求宝付慧收钱失败,请求参数:{},慧收钱状态码:{},慧收钱错误信息:{}",req.toString(),result.getErrorCode(),result.getErrorMsg());
        }

        return result;
    }

    private void verifySign(Object response,String expectSign){
        SimplePropertyPreFilter filter = new SimplePropertyPreFilter();
        filter.getExcludes().add("sign");
        String respJson = JSON.toJSONString(response, filter);
        JSONObject jsonObject = JSON.parseObject(respJson);
        String signContent = jsonObject.entrySet().stream().filter(x -> !x.getKey().equals("sign"))
                .map(x -> x.getKey() + "=" + x.getValue())
                .collect(Collectors.joining("&"));
        String actualSign = new String(DigestUtils.md5Digest(RsaUtil.base64Encode(signContent).getBytes()));
        if(StringUtils.isBlank(expectSign) || !expectSign.equals(actualSign)){
            log.info("expect sign:{},actual sign:{}",expectSign,actualSign);
            throw new RuntimeException("验签不通过！");
        }
    }

    private void signReq(BaseHsqReq req){
        String reqJson = JSON.toJSONString(req);
        JSONObject jsonObject = JSON.parseObject(reqJson);
        String signContent = jsonObject.entrySet().stream().filter(x -> x.getKey().equals("sign"))
                .map(x -> x.getKey() + "=" + x.getValue())
                .collect(Collectors.joining("&"));

        byte[] bytes = DigestUtils.md5Digest(Base64.encode(signContent, Charset.forName("utf-8")).getBytes());
        req.setSign(new String(bytes,Charset.forName("utf-8")));
    }

    public static void main(String[] args) {
//        int []a = new int[]{1,2};
//        List<int[]> ints = Arrays.asList(a);
//        System.out.println(ints.get(0).getClass().getSimpleName());
//        String[] myArray1 = { "1", "2", "3" };
//        List myList1 = Arrays.asList(myArray1);
//        System.out.println("数组大小为：" + myList1.size());
//        String s = "{\"extend\":\"\",\"orderAmt\":\"1\",\"orderStatus\":\"PROCESSING\",\"payType\":\"WECHAT_JSAPI\",\"qrCode\":\"{\\\\\\\"appId\\\\\\\":\\\\\\\"wx6891e1111111111\\\\\\\",\\\\\\\"timeStamp\\\\\\\":\\\\\\\"1609899474\\\\\\\",\\\\\\\"nonceStr\\\\\\\":\\\\\\\"b0b185ccf3fd4d0db1445032594a855c\\\\\\\",\\\\\\\"package\\\\\\\":\\\\\\\"prepay_id=wx06101754244111111111111111\\\\\\\",\\\\\\\"signType\\\\\\\":\\\\\\\"RSA\\\\\\\",\\\\\\\"paySign\\\\\\\":\\\\\\\"DOKv2eyI07nR8ofMa/DdtBGCwPxAVsDpIUrbQKSdAmlxINdnTFHPrNW+ycezxTa/sH71V39h2g95Mxna+/c9h6e01MrEpfjfA5XR4xNwGMvg215XJS3NlJIZ3fgpYkhKXeOHewd3HgYAknBVj5tg1MZSLvc/Jg7H2Zal+jgfOTud3y0ttFBVtKGZc+K4L7I8NistXvvNPHoV12S1gqIiosYubxmiNo2ZfcEPWx1n6DpKSTuSSP4wN8bdVIQvK38InMaIHKEUzjUTXdmMjeZkZW5h1cUZqmUkexRGIqcBdAS1txodECA5QvPcu2hTAaiMjGVfhD/t5IxpHYUcA7wCVg==\\\\\\\"}\",\"respCode\":\"100201\",\"respMsg\":\"交易处理中，请稍后查询\",\"tradeNo\":\"21000020210202211607003487326263\",\"transNo\":\"DD20210224091454927\"}";
//        TradePreCreateRespDTO tradePreCreateRespDTO = JSON.parseObject(s, TradePreCreateRespDTO.class);
//        System.out.println(tradePreCreateRespDTO);

        WeChatPaymentNotifyDTO weChatPaymentNotifyDTO = new WeChatPaymentNotifyDTO();
        Object o = weChatPaymentNotifyDTO;
        System.out.println(o instanceof WeChatPaymentNotifyDTO);
        o = new CallbackResponseDTO();
        System.out.println(o instanceof CallbackResponseDTO);
        System.out.println(o.getClass().getSimpleName());
    }
}
